--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
meshchatx/android_codec2.py e41daa5b65e07a5baa1aaad32d97f90482f1cd1c (e41daa5b) Text, 8.38 KB
T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""Load Codec2 native libraries before pycodec2/LXST on Chaquopy Android."""
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72import T7ee787ctypes
Tff7b72import T7ee787importlib
Tff7b72import T7ee787logging
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72from T7ee787pathlib Tff7b72import Te6edf3Path
Te6edf3logger Tff7b72= Te6edf3loggingTff7b72.Td2a8ffgetLoggerTb4b4b4(Tff7b72__name__Tb4b4b4)
Te6edf3_codec2_preload_errorTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72None
Te6edf3_codec2_preload_ok Tff7b72= Tff7b72False
Te6edf3_codec2_preload_attempted Tff7b72= Tff7b72False
Tff7b72def Td2a8ff_is_chaquopy_androidTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72import T7ee787java T8b949e# noqa: F401
Tff7b72except Tf85149ImportErrorTb4b4b4:
Tff7b72return Tff7b72False
Tff7b72return Tff7b72True
Tff7b72def Td2a8ff_cdll_loadTb4b4b4(Te6edf3path_or_nameTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
T8b949e"""Load a shared library with RTLD_GLOBAL when the platform supports it.
pycodec2.so declares NEEDED libcodec2.so. Loading with RTLD_GLOBAL lets the
later dlopen of the extension resolve that dependency.
"""
Te6edf3mode Tff7b72= Tffa657getattrTb4b4b4(Te6edf3ctypesTb4b4b4, Ta5d6ff"Ta5d6ffRTLD_GLOBALTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72if Te6edf3mode Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3ctypesTff7b72.Td2a8ffCDLLTb4b4b4(Te6edf3path_or_nameTb4b4b4)
Tff7b72return Te6edf3ctypesTff7b72.Td2a8ffCDLLTb4b4b4(Te6edf3path_or_nameTb4b4b4, Te6edf3modeTff7b72=Te6edf3modeTb4b4b4)
Tff7b72def Td2a8ff_libcodec2_candidatesTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657listTb4b4b4[Te6edf3PathTb4b4b4]Tb4b4b4:
T8b949e"""Return candidate paths for libcodec2.so without importing pycodec2.
import pycodec2 loads the extension which already needs libcodec2.so.
Searching sys.path on disk avoids that chicken-and-egg failure.
"""
Te6edf3candidatesTb4b4b4: Tffa657listTb4b4b4[Te6edf3PathTb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3seenTb4b4b4: Tffa657setTb4b4b4[Tffa657strTb4b4b4] Tff7b72= Tffa657setTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffaddTb4b4b4(Te6edf3pathTb4b4b4: Te6edf3PathTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
Te6edf3key Tff7b72= Tffa657strTb4b4b4(Te6edf3pathTb4b4b4)
Tff7b72if Te6edf3key Tff7b72in Te6edf3seenTb4b4b4:
Tff7b72return
Te6edf3seenTff7b72.Td2a8ffaddTb4b4b4(Te6edf3keyTb4b4b4)
Te6edf3candidatesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3pathTb4b4b4)
Te6edf3explicit Tff7b72= Te6edf3PathTb4b4b4(Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffMESHCHAT_LIBCODEC2_PATHTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3explicitTff7b72.Td2a8ffis_fileTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3addTb4b4b4(Te6edf3explicitTb4b4b4)
Tff7b72for Te6edf3entry Tff7b72in Te6edf3sysTff7b72.Td2a8ffpathTb4b4b4:
Tff7b72if Tff7b72not Te6edf3entryTb4b4b4:
Tff7b72continue
Te6edf3root Tff7b72= Te6edf3PathTb4b4b4(Te6edf3entryTb4b4b4)
Te6edf3addTb4b4b4(Te6edf3root Tff7b72/ Ta5d6ff"Ta5d6ffpycodec2Ta5d6ff" Tff7b72/ Ta5d6ff"Ta5d6fflibcodec2.soTa5d6ff"Tb4b4b4)
Te6edf3addTb4b4b4(Te6edf3root Tff7b72/ Ta5d6ff"Ta5d6ffchaquopyTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6fflibTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6fflibcodec2.soTa5d6ff"Tb4b4b4)
T8b949e# Some Chaquopy layouts nest native libs under site-packages directly.
Te6edf3addTb4b4b4(Te6edf3root Tff7b72/ Ta5d6ff"Ta5d6fflibcodec2.soTa5d6ff"Tb4b4b4)
T8b949e# Extracted APK native lib dirs (ABI-specific jniLibs sync target).
Tff7b72for Te6edf3env_key Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffMESHCHAT_NATIVE_LIB_DIRTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffANDROID_NATIVE_LIBRARY_DIRTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3native_dir Tff7b72= Te6edf3PathTb4b4b4(Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Te6edf3env_keyTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3native_dirTff7b72.Td2a8ffis_dirTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3addTb4b4b4(Te6edf3native_dir Tff7b72/ Ta5d6ff"Ta5d6fflibcodec2.soTa5d6ff"Tb4b4b4)
Tff7b72return Te6edf3candidates
Tff7b72def Td2a8ff_java_system_load_libraryTb4b4b4(Te6edf3nameTb4b4b4: Tffa657str Tff7b72= Ta5d6ff"Ta5d6ffcodec2Ta5d6ff"Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Ask the Android runtime to load a jniLibs shared object by soname."""
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787javaT7ee787.T7ee787lang Tff7b72import Te6edf3System Tff7b72as Te6edf3JavaSystem
Te6edf3JavaSystemTff7b72.Td2a8ffloadLibraryTb4b4b4(Te6edf3nameTb4b4b4)
Tff7b72return Tff7b72True
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffdebugTb4b4b4(Ta5d6ff"Ta5d6ffJava System.loadLibrary(Tffd700%sTa5d6ff) failed: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3nameTb4b4b4, Te6edf3excTb4b4b4)
Tff7b72return Tff7b72False
Tff7b72def Td2a8ff_java_system_load_absoluteTb4b4b4(Te6edf3pathTb4b4b4: Te6edf3PathTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Load an absolute .so path via Java System.load."""
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787javaT7ee787.T7ee787lang Tff7b72import Te6edf3System Tff7b72as Te6edf3JavaSystem
Te6edf3JavaSystemTff7b72.Td2a8ffloadTb4b4b4(Tffa657strTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4)
Tff7b72return Tff7b72True
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffdebugTb4b4b4(Ta5d6ff"Ta5d6ffJava System.load(Tffd700%sTa5d6ff) failed: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3pathTb4b4b4, Te6edf3excTb4b4b4)
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffensure_codec2_native_libraryTb4b4b4(Tff7b72*Tb4b4b4, Te6edf3forceTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Preload libcodec2.so so import pycodec2 works on Android.
Chaquopy installs chaquopy-libcodec2 separately from pycodec2. The
extension module only declares a NEEDED entry for libcodec2.so. Without
preloading or bundling the shared library next to pycodec2.so, imports
fail at runtime with dlopen errors.
Always prefer absolute-path loads with RTLD_GLOBAL even after a successful
Java System.loadLibrary. On some Android or Chaquopy linker setups the
Java load alone is not enough for the Python extension dlopen.
"""
Tff7b72global Te6edf3_codec2_preload_attemptedTb4b4b4, Te6edf3_codec2_preload_errorTb4b4b4, Te6edf3_codec2_preload_ok
Tff7b72if Te6edf3_codec2_preload_ok Tff7b72and Tff7b72not Te6edf3forceTb4b4b4:
Tff7b72return Tff7b72True
Tff7b72if Te6edf3_codec2_preload_attempted Tff7b72and Tff7b72not Te6edf3force Tff7b72and Te6edf3_codec2_preload_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
T8b949e# Previous attempt failed. Allow one more try when force=True only.
Tff7b72return Tff7b72False
Te6edf3_codec2_preload_attempted Tff7b72= Tff7b72True
Te6edf3_codec2_preload_error Tff7b72= Tff7b72None
Tff7b72if Tff7b72not Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3_codec2_preload_ok Tff7b72= Tff7b72True
Tff7b72return Tff7b72True
Te6edf3loaded_any Tff7b72= Tff7b72False
Tff7b72if Te6edf3_java_system_load_libraryTb4b4b4(Ta5d6ff"Ta5d6ffcodec2Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffinfoTb4b4b4(Ta5d6ff"Ta5d6ffLoaded Codec2 via Java System.loadLibrary(codec2)Ta5d6ff"Tb4b4b4)
Te6edf3loaded_any Tff7b72= Tff7b72True
Tff7b72tryTb4b4b4:
Te6edf3_cdll_loadTb4b4b4(Ta5d6ff"Ta5d6fflibcodec2.soTa5d6ff"Tb4b4b4)
Te6edf3loaded_any Tff7b72= Tff7b72True
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72pass
Te6edf3last_errorTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72None
Tff7b72for Te6edf3lib_path Tff7b72in Te6edf3_libcodec2_candidatesTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3lib_pathTff7b72.Td2a8ffis_fileTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Te6edf3_java_system_load_absoluteTb4b4b4(Te6edf3lib_pathTb4b4b4)Tb4b4b4:
Te6edf3loaded_any Tff7b72= Tff7b72True
Te6edf3loggerTff7b72.Td2a8ffinfoTb4b4b4(Ta5d6ff"Ta5d6ffLoaded Codec2 via Java System.load(Tffd700%sTa5d6ff)Ta5d6ff"Tb4b4b4, Te6edf3lib_pathTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3_cdll_loadTb4b4b4(Tffa657strTb4b4b4(Te6edf3lib_pathTb4b4b4)Tb4b4b4)
Te6edf3loggerTff7b72.Td2a8ffinfoTb4b4b4(Ta5d6ff"Ta5d6ffLoaded Codec2 native library from Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3lib_pathTb4b4b4)
Te6edf3loaded_any Tff7b72= Tff7b72True
Tff7b72break
Tff7b72except Tf85149OSError Tff7b72as Te6edf3excTb4b4b4:
Te6edf3last_error Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3lib_pathTffd700}Ta5d6ff: Tffd700{Te6edf3excTffd700}Ta5d6ff"
Tff7b72if Te6edf3loaded_anyTb4b4b4:
Te6edf3_codec2_preload_ok Tff7b72= Tff7b72True
Te6edf3_codec2_preload_error Tff7b72= Tff7b72None
Tff7b72return Tff7b72True
Te6edf3_codec2_preload_ok Tff7b72= Tff7b72False
Te6edf3_codec2_preload_error Tff7b72= Te6edf3last_error Tff7b72or Ta5d6ff"Ta5d6fflibcodec2.so not found on AndroidTa5d6ff"
Te6edf3loggerTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffCodec2 native preload failed: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3_codec2_preload_errorTb4b4b4)
Tff7b72return Tff7b72False
Tff7b72def Td2a8ff_install_pycodec2_ctypes_fallbackTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657tupleTb4b4b4[Tffa657boolTb4b4b4, Tffa657str Tff7b72| Tff7b72NoneTb4b4b4]Tb4b4b4:
T8b949e"""Install a ctypes pycodec2 stand-in when the Cython extension cannot load.
Some Android vendor wheels ship an empty pycodec2.so (no PyInit). libcodec2.so
is still present. Expose the same Codec2 API LXST expects via ctypes.
"""
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatx Tff7b72import Te6edf3pycodec2_ctypes
Te6edf3okTb4b4b4, Te6edf3err Tff7b72= Te6edf3pycodec2_ctypesTff7b72.Td2a8ffprobeTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3okTb4b4b4:
Tff7b72return Tff7b72FalseTb4b4b4, Te6edf3err
T8b949e# Drop a broken partial import so LXST picks up the stand-in.
Te6edf3sysTff7b72.Td2a8ffmodulesTff7b72.Td2a8ffpopTb4b4b4(Ta5d6ff"Ta5d6ffpycodec2Ta5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Te6edf3sysTff7b72.Td2a8ffmodulesTb4b4b4[Ta5d6ff"Ta5d6ffpycodec2Ta5d6ff"Tb4b4b4] Tff7b72= Te6edf3pycodec2_ctypes
Te6edf3loggerTff7b72.Td2a8ffwarningTb4b4b4(
Ta5d6ff"Ta5d6ffUsing ctypes Codec2 fallback (native pycodec2 extension unavailable)Ta5d6ff"
Tb4b4b4)
Tff7b72return Tff7b72TrueTb4b4b4, Tff7b72None
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Tff7b72return Tff7b72FalseTb4b4b4, Tffa657strTb4b4b4(Te6edf3excTb4b4b4)
Tff7b72def Td2a8ffprobe_pycodec2Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657tupleTb4b4b4[Tffa657boolTb4b4b4, Tffa657str Tff7b72| Tff7b72NoneTb4b4b4]Tb4b4b4:
T8b949e"""Import pycodec2 after preload and report whether Codec2 works."""
Tff7b72if Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4) Tff7b72and Tff7b72not Te6edf3ensure_codec2_native_libraryTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e# Retry once in case native libs appeared after an early failed attempt.
Tff7b72if Tff7b72not Te6edf3ensure_codec2_native_libraryTb4b4b4(Te6edf3forceTff7b72=Tff7b72TrueTb4b4b4)Tb4b4b4:
T8b949e# Still try ctypes against whatever path we can find.
Tff7b72return Te6edf3_install_pycodec2_ctypes_fallbackTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72import T7ee787pycodec2
Te6edf3c2 Tff7b72= Te6edf3pycodec2Tff7b72.Td2a8ffCodec2Tb4b4b4(T79c0ff1600Tb4b4b4)
Te6edf3_ Tff7b72= Te6edf3c2Tff7b72.Td2a8ffsamples_per_frameTb4b4b4(Tb4b4b4)
Tff7b72return Tff7b72TrueTb4b4b4, Tff7b72None
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Te6edf3native_error Tff7b72= Tffa657strTb4b4b4(Te6edf3excTb4b4b4)
Tff7b72if Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3okTb4b4b4, Te6edf3fallback_error Tff7b72= Te6edf3_install_pycodec2_ctypes_fallbackTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3okTb4b4b4:
Tff7b72return Tff7b72TrueTb4b4b4, Tff7b72None
Tff7b72return Tff7b72FalseTb4b4b4, Te6edf3fallback_error Tff7b72or Te6edf3native_error
Tff7b72return Tff7b72FalseTb4b4b4, Te6edf3native_error
Tff7b72def Td2a8ffensure_lxst_codec2_bindingTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Ensure LXST.Codecs.Codec2 is bound after a successful pycodec2 probe.
Soft-import patches set Codec2 to None when the first LXST.Codecs import
fails. Reload Codecs after preload so telephony can use Codec2 modes.
"""
Te6edf3okTb4b4b4, Te6edf3_err Tff7b72= Te6edf3probe_pycodec2Tb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3okTb4b4b4:
Tff7b72return Tff7b72False
Tff7b72tryTb4b4b4:
Te6edf3codecs_mod Tff7b72= Te6edf3importlibTff7b72.Td2a8ffimport_moduleTb4b4b4(Ta5d6ff"Ta5d6ffLXST.CodecsTa5d6ff"Tb4b4b4)
Tff7b72if Tffa657getattrTb4b4b4(Te6edf3codecs_modTb4b4b4, Ta5d6ff"Ta5d6ffCodec2Ta5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4) Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72True
Te6edf3codecs_mod Tff7b72= Te6edf3importlibTff7b72.Td2a8ffreloadTb4b4b4(Te6edf3codecs_modTb4b4b4)
Tff7b72if Tffa657getattrTb4b4b4(Te6edf3codecs_modTb4b4b4, Ta5d6ff"Ta5d6ffCodec2Ta5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4) Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72False
T8b949e# Refresh Telephony bindings that may have imported Codec2 as None.
Tff7b72tryTb4b4b4:
Te6edf3telephony_mod Tff7b72= Te6edf3importlibTff7b72.Td2a8ffimport_moduleTb4b4b4(Ta5d6ff"Ta5d6ffLXST.Primitives.TelephonyTa5d6ff"Tb4b4b4)
Te6edf3importlibTff7b72.Td2a8ffreloadTb4b4b4(Te6edf3telephony_modTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3tel_excTb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffdebugTb4b4b4(Ta5d6ff"Ta5d6ffLXST Telephony reload skipped: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3tel_excTb4b4b4)
Tff7b72return Tff7b72True
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Te6edf3loggerTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffLXST Codec2 binding failed: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3excTb4b4b4)
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffcodec2_preload_errorTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Return the last preload failure message, if any."""
Tff7b72return Te6edf3_codec2_preload_error
Tff7b72def Td2a8ffreset_codec2_preload_state_for_testsTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
T8b949e"""Clear preload memoization (tests only)."""
Tff7b72global Te6edf3_codec2_preload_attemptedTb4b4b4, Te6edf3_codec2_preload_errorTb4b4b4, Te6edf3_codec2_preload_ok
Te6edf3_codec2_preload_attempted Tff7b72= Tff7b72False
Te6edf3_codec2_preload_ok Tff7b72= Tff7b72False
Te6edf3_codec2_preload_error Tff7b72= Tff7b72None
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────